home *** CD-ROM | disk | FTP | other *** search
/ APDL Other Worlds / APDL Other Worlds Collection.iso / SF3000 / Extras / !SFskyedit / c / EditSky < prev    next >
Encoding:
Text File  |  2003-10-16  |  59.5 KB  |  1,594 lines

  1. /*
  2.  *  SFskyedit - Star Fighter 3000 sky colours editor
  3.  *  Sky editing window
  4.  *  Copyright (C) 2001  Chris Bazley
  5.  *
  6.  *  This program is free software; you can redistribute it and/or modify
  7.  *  it under the terms of the GNU General Public Licence as published by
  8.  *  the Free Software Foundation; either version 2 of the Licence, or
  9.  *  (at your option) any later version.
  10.  *
  11.  *  This program is distributed in the hope that it will be useful,
  12.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  *  GNU General Public Licence for more details.
  15.  *
  16.  *  You should have received a copy of the GNU General Public Licence
  17.  *  along with this program; if not, write to the Free Software
  18.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  */
  20.  
  21. /* ANSI library files */
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdbool.h>
  26. #include <limits.h>
  27. #include <time.h>
  28. #include <assert.h>
  29.  
  30. /* RISC OS library files */
  31. #include "kernel.h"
  32. #include "swis.h"
  33. #include "wimp.h"
  34. #include "toolbox.h"
  35. #include "event.h"
  36. #include "wimplib.h"
  37. #include "window.h"
  38. #include "gadgets.h"
  39. #include "flex.h"
  40.  
  41. /* My library files */
  42. #include "err.h"
  43. #include "msgtrans.h"
  44. #include "Macros.h"
  45. #include "Loader.h"
  46. #include "ViewsMenu.h"
  47. #include "SFformats.h"
  48. #include "Pal256.h"
  49. #include "NullPoll.h"
  50.  
  51. /* Local headers */
  52. #include "Utils.h"
  53. #include "SFSFileInfo.h"
  54. #include "SFSSavebox.h"
  55. #include "DCS_dialogue.h"
  56. #include "Menus.h"
  57. #include "Preview.h"
  58. #include "Back-end.h"
  59. #include "Insert.h"
  60. #include "Interpolate.h"
  61. #include "EditSky.h"
  62. #include "Main.h"
  63.  
  64. /* Key shortcuts */
  65. #define KEY_FILEINFO    0x01
  66. #define KEY_CLOSEFILE   0x02
  67. #define KEY_SAVEFILE    0x04
  68. #define KEY_SMOOTHSEL   0x05
  69. #define KEY_SELALL      0x06
  70. #define KEY_CLEARSEL    0x07
  71. #define KEY_EDITCOLS    0x08
  72. #define KEY_PREVIEW     0x0f
  73. #define KEY_UP          0x10
  74. #define KEY_DOWN        0x11
  75. #define KEY_PAGEUP      0x12
  76. #define KEY_PAGEDOWN    0x13
  77. #define KEY_INSERT      0x14
  78. #define KEY_COPY        0x15
  79. #define KEY_CUT         0x16
  80. #define KEY_PASTE       0x17
  81. #define KEY_DELETE      0x18
  82. #define KEY_INTERPOLATE 0x19
  83. #define KEY_HOME        0x20
  84. #define KEY_END         0x21
  85. #define KEY_TRAP        0x22
  86.  
  87. /* Gadgets */
  88. #define GADGET_MINSKY  0x00
  89. #define GADGET_MINSTAR 0x01
  90.  
  91. #define DRAGGING_AREA 1
  92. #define DRAGGING_SEL  2
  93.  
  94. #define SCROLL_BORDER 64
  95. #define PANE_HEIGHT   140
  96. #define WIN_HEIGHT    3180
  97. #define WIN_WIDTH     548
  98. #define COL_HEIGHT    32
  99. #define GAP           16
  100.  
  101. typedef char number[4];
  102. static int dragging_bands;
  103. static bool null_polling;
  104. static ViewData *drag_source;
  105. static int ghost_caret_win;
  106. static ViewData *ghost_caret_view;
  107. static int ghost_caret_position;
  108. static bool dragging_shift;
  109.  
  110. bool def_trap_caret = true; /* state to be set for new views */
  111.  
  112. /* ----------------------------------------------------------------------- */
  113. /*                       Function prototypes                               */
  114.  
  115. static ToolboxEventHandler _EditSky_keyhandler, _EditSky_minskychanged, _EditSky_starheightchanged, _EditSky_deleted, _EditSky_colourselhandler;
  116.  
  117. static WimpEventHandler _EditSky_clickhandler, _EditSky_closing, _EditSky_opening, _EditSky_redrawhandler, _EditSky_dragscroll, _EditSky_dragended, _EditSky_caretwatch, _EditSky_escwatch;
  118.  
  119. static LoaderFinishedHandler _EditSky_loadfile;
  120. static _kernel_oserror *_EditSky_plotcursor(int x, int y, int col);
  121. static _kernel_oserror *_EditSky_redrawrows(int window_handle, int start_band, int end_band);
  122. static _kernel_oserror *_EditSky_scroll(ViewData *data, int scrollmod, int page);
  123. static void _EditSky_dragstopped(void);
  124. static _kernel_oserror *_EditSky_redrawcaret(int window_handle, int caret_position);
  125. static int prepare_to_insert(ViewData *view_data);
  126. static _kernel_oserror *scroll_win_for_caret(ViewData *view_data, int window_handle);
  127. static _kernel_oserror *scroll_caret_for_win(ViewData *view_data, WimpOpenWindowBlock *win_info);
  128.  
  129. /* ----------------------------------------------------------------------- */
  130. /*                         Public functions                                */
  131.  
  132. ObjectId EditSky_create(SF_SkyColours **sky, char *title, bool title_is_file)
  133. {
  134.   ViewData *view_data;
  135.  
  136.   /* Grab memory */
  137.   view_data = malloc(sizeof(ViewData));
  138.   if(view_data == NULL)
  139.     MG_RETV("NoMem", NULL_ObjectId) /* failed */
  140.  
  141.   if(!flex_alloc((flex_ptr)&view_data->icon_validations, 63*sizeof(colstring))) {
  142.     MG("NoMem"); /* failed */
  143.     goto err_exit1;
  144.   }
  145.   if(!flex_alloc((flex_ptr)&view_data->icon_FGcols, 63)) {
  146.     MG("NoMem"); /* failed */
  147.     goto err_exit2;
  148.   }
  149.  
  150.   /* Create object and get object IDs of attached objects */
  151.   if(E(toolbox_create_object(0, "EditSky", &view_data->window_id)))
  152.     goto err_exit3;
  153.   if(E(window_get_tool_bars(Window_InternalTopLeftToolbar, view_data->window_id, NULL, &view_data->dispvalues_pane, NULL, NULL)))
  154.     goto err_exit4;
  155.   if(E(window_get_menu(0, view_data->window_id, &view_data->menu)))
  156.     goto err_exit4;
  157.   view_data->preview_id = NULL_ObjectId;
  158.   if(E(toolbox_set_client_handle(0, view_data->window_id, view_data)))
  159.     goto err_exit4;
  160.  
  161.   /* Add entry to iconbar menu (proper title will be filled in presently) */
  162.   if(E(ViewsMenu_add(view_data->window_id, "", "")))
  163.     goto err_exit4;
  164.  
  165.   view_data->last_savepath = NULL;
  166.   if(!EditSky_newfile(view_data, title, title_is_file))
  167.     goto err_exit5;
  168.  
  169.   /* Set displayed values from skyfile header */
  170.   if(E(numberrange_set_value(0, view_data->dispvalues_pane, GADGET_MINSKY, (*sky)->min_sky_height))
  171.   || E(numberrange_set_value(0, view_data->dispvalues_pane, GADGET_MINSTAR, (*sky)->min_stars_height)))
  172.     goto err_exit5;
  173.  
  174.   /* Register listener for files on our window */
  175.   if(E(loader_register_listener(0, FILETYPE_SKYCOLS, view_data->window_id, NULL, load_compressed, _EditSky_loadfile, view_data)))
  176.     goto err_exit5;
  177.  
  178.   /* Register Wimp event handlers */
  179.   if(E(event_register_wimp_handler(view_data->window_id, Wimp_EMouseClick, _EditSky_clickhandler, view_data))
  180.   || E(event_register_wimp_handler(view_data->window_id, Wimp_ERedrawWindow, _EditSky_redrawhandler, view_data))
  181.   || E(event_register_wimp_handler(view_data->window_id, Wimp_ECloseWindow, _EditSky_closing, view_data))
  182.   || E(event_register_wimp_handler(view_data->window_id, Wimp_EOpenWindow, _EditSky_opening, view_data))
  183.   || E(event_register_wimp_handler(-1, Wimp_ENull, _EditSky_dragscroll, view_data))
  184.   || E(event_register_wimp_handler(-1, Wimp_EUserDrag, _EditSky_dragended, view_data))
  185.   || E(event_register_wimp_handler(view_data->window_id, -1, _EditSky_caretwatch, view_data)))
  186.     goto err_exit5;
  187.  
  188.   /* Register Toolbox event handlers
  189.      Note that ObjectDeleted handler is registered AFTER anything
  190.      that could cause an error and therefore premature deletion! */
  191.   if(!E(event_register_toolbox_handler(view_data->window_id, -1, _EditSky_keyhandler, view_data))
  192.   && !E(event_register_toolbox_handler(pal256_sharedid, Pal256_ColourSelected, _EditSky_colourselhandler, view_data))
  193.   && !E(event_register_toolbox_handler(view_data->dispvalues_pane, NumberRange_ValueChanged, _EditSky_minskychanged, view_data))
  194.   && !E(event_register_toolbox_handler(view_data->dispvalues_pane, NumberRange_ValueChanged, _EditSky_starheightchanged, view_data))
  195.   && !E(event_register_toolbox_handler(view_data->window_id, -1, _EditSky_keyhandler, view_data))
  196.   && !E(event_register_toolbox_handler(view_data->window_id, Toolbox_ObjectDeleted, _EditSky_deleted, view_data))) {
  197.  
  198.     /* Its now safe to re-anchor sky data from source */
  199.     flex_reanchor((flex_ptr)&view_data->sky, (flex_ptr)sky);
  200.  
  201.     /* Set colours to display */
  202.     EditSky_rowsupdated(view_data, 0, 62);
  203.  
  204.     /* Init selection model */
  205.     view_data->selection_exists = false;
  206.     view_data->caret_position = 0;
  207.     view_data->has_input_focus = true;
  208.  
  209.     view_data->trap_caret = def_trap_caret;
  210.  
  211.     return view_data->window_id; /* success */
  212.   }
  213.  
  214.   /* Rudimentary attempt to recover from error */
  215.   err_exit5:
  216.     RE(ViewsMenu_remove(view_data->window_id))
  217.   err_exit4:
  218.     RE(toolbox_delete_object(0, view_data->window_id))
  219.   err_exit3:
  220.     flex_free((flex_ptr)&view_data->icon_FGcols);
  221.   err_exit2:
  222.     flex_free((flex_ptr)&view_data->icon_validations);
  223.   err_exit1:
  224.     free(view_data);
  225.     return NULL_ObjectId; /* failed */
  226. }
  227.  
  228. /* ----------------------------------------------------------------------- */
  229.  
  230. void EditSky_paste(ViewData *view_data)
  231. {
  232.   if(clipboard != NULL) {
  233.     int insertion_pos = prepare_to_insert(view_data);
  234.  
  235.     insert_from_clipboard(&view_data->sky, insertion_pos);
  236.  
  237.     /* Select newly inserted rows */
  238.     view_data->selection_start = insertion_pos;
  239.     view_data->selection_end = insertion_pos + clipboard_size - 1;
  240.     view_data->selection_exists = true;
  241.     EditSky_rowsupdated(view_data, view_data->selection_start, 62);
  242.     EditSky_markaschanged(view_data);
  243.   }
  244. }
  245.  
  246. /* ----------------------------------------------------------------------- */
  247.  
  248. void EditSky_remove_sel(ViewData *view_data)
  249. {
  250.   remove_bands(&view_data->sky, view_data->selection_start, view_data->selection_end);
  251.   view_data->selection_exists = false;
  252.   view_data->caret_position = view_data->selection_start;
  253.   EditSky_rowsupdated(view_data, view_data->selection_start, 62);
  254.  
  255.   int window_handle;
  256.   if(!E(window_get_wimp_handle(0, view_data->window_id, &window_handle)))
  257.     RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  258.   //if(view_data->trap_caret)
  259.     RE(scroll_win_for_caret(view_data, window_handle))
  260.  
  261.   EditSky_markaschanged(view_data);
  262. }
  263.  
  264. /* ----------------------------------------------------------------------- */
  265.  
  266. void EditSky_preview(ViewData *view_data)
  267. {
  268.   /* Open a preview window, creating if necessary */
  269.   if(view_data->preview_id == NULL) {
  270.     view_data->preview_id = Preview_create();
  271.     if(view_data->preview_id == NULL)
  272.       return;
  273.   }
  274.   RE(window_set_title(0, view_data->preview_id, msgs_lookup_sub1("PrevTitle", tail(view_data->last_savepath, 1))))
  275.   RE(ViewsMenu_show_object(0, view_data->preview_id, Toolbox_ShowObject_AtPointer, NULL, view_data->window_id, NULL_ComponentId)) /* (may have been iconised) */
  276. }
  277.  
  278. /* ----------------------------------------------------------------------- */
  279.  
  280. void EditSky_set_starheight(ObjectId objectref, int height)
  281. {
  282.   /* Set internal and displayed data to new star height */
  283.   ViewData *view_data;
  284.  
  285.   E_RET(toolbox_get_client_handle(0, objectref, (void **)&view_data))
  286.  
  287.   view_data->sky->min_stars_height = height;
  288.   RE(numberrange_set_value(0, view_data->dispvalues_pane, GADGET_MINSTAR, height))
  289.  
  290.   /* Re-render sky preview (if any) */
  291.   if(view_data->preview_id != NULL)
  292.     Preview_rendersky(view_data->preview_id);
  293.  
  294.   /* Mark as unsaved changes */
  295.   EditSky_markaschanged(view_data);
  296. }
  297.  
  298. /* ----------------------------------------------------------------------- */
  299.  
  300. void EditSky_set_minheight(ObjectId objectref, int height)
  301. {
  302.   /* Set internal and displayed data to new min sky height */
  303.   ViewData *view_data;
  304.  
  305.   E_RET(toolbox_get_client_handle(0, objectref, (void **)&view_data))
  306.  
  307.   view_data->sky->min_sky_height = view_data->sky->min_sky_height + height;
  308.   RE(numberrange_set_value(0, view_data->dispvalues_pane, GADGET_MINSKY, view_data->sky->min_sky_height))
  309.  
  310.   /* Re-render sky preview (if any) */
  311.   if(view_data->preview_id != NULL)
  312.     Preview_rendersky(view_data->preview_id);
  313.  
  314.   /* Mark as unsaved changes */
  315.   EditSky_markaschanged(view_data);
  316. }
  317.  
  318. /* ----------------------------------------------------------------------- */
  319.  
  320. void EditSky_clearselection(ViewData *view_data)
  321. {
  322.   if(!view_data->selection_exists)
  323.     return;
  324.  
  325.   int window_handle;
  326.   E_RET(window_get_wimp_handle(0, view_data->window_id, &window_handle));
  327.  
  328.   /* Undraw selection */
  329.   RE(_EditSky_redrawrows(window_handle, view_data->selection_start, view_data->selection_end))
  330.  
  331.   /* Set caret to nearest end of selection */
  332.   if(absdiff(view_data->caret_position, view_data->selection_start) < absdiff((view_data->caret_position-1), view_data->selection_end))
  333.     view_data->caret_position = view_data->selection_start;
  334.   else
  335.     view_data->caret_position = view_data->selection_end+1;
  336.   view_data->selection_exists = false;
  337.  
  338.   /* Draw caret (must have either selection or caret) */
  339.   RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  340.   
  341.   //if(view_data->trap_caret)
  342.     RE(scroll_win_for_caret(view_data, window_handle))  
  343. }
  344.  
  345. /* ----------------------------------------------------------------------- */
  346.  
  347. void EditSky_rowsupdated(ViewData *view_data, int start_band, int end_band)
  348. {
  349.   /* Re-render sky preview (if any) */
  350.   if(view_data->preview_id != NULL)
  351.     Preview_rendersky(view_data->preview_id);
  352.  
  353.   /* Update cached data used for drawing icons */
  354.   for(int band = start_band; band <= end_band; band++) {
  355.     unsigned int real_colour = palette[get_shade(&view_data->sky, band)];
  356.     /* background */
  357.     sprintf((char *)(view_data->icon_validations[band]), "C/%X", (real_colour & 0xffffff00) >> 8);
  358.     /* foreground */
  359.     if(brightness_of_24bit_col(real_colour) > 128)
  360.       view_data->icon_FGcols[band] = 0x07; /* Black number */
  361.     else
  362.       view_data->icon_FGcols[band] = 0x00; /* White number */
  363.   }
  364.  
  365.   /* Force redraw of specified colour bands */
  366.   int window_handle;
  367.   E_RET(window_get_wimp_handle(0, view_data->window_id, &window_handle));
  368.   RE(_EditSky_redrawrows(window_handle, start_band, end_band))
  369. }
  370.  
  371. /* ----------------------------------------------------------------------- */
  372.  
  373. void EditSky_openparentdir(ViewData *view_data)
  374. {
  375.   /* Open parent directory */
  376.   char *last_dot = strrchr(view_data->last_savepath, (int)'.');
  377.   if(last_dot == NULL)
  378.     return;
  379.   int nchars = (int)(last_dot - view_data->last_savepath);
  380.  
  381.   char command_buffer[14 + nchars + 1];
  382.   strcpy(command_buffer, "Filer_OpenDir ");
  383.   strncat(command_buffer, view_data->last_savepath, nchars);
  384.  
  385.   if(_kernel_oscli(command_buffer) == _kernel_ERROR)
  386.     err_check_rep(_kernel_last_oserror());
  387. }
  388.  
  389. /* ----------------------------------------------------------------------- */
  390. /*                         Private functions                               */
  391.  
  392. static _kernel_oserror *_EditSky_redrawrows(int window_handle, int start_band, int end_band)
  393. {
  394.   return wimp_force_redraw(window_handle, 0, -WIN_HEIGHT + start_band*(COL_HEIGHT+GAP) + GAP/2, WIN_WIDTH, -WIN_HEIGHT + (end_band+1)*(COL_HEIGHT+GAP) + GAP/2);
  395. }
  396.  
  397. /* ----------------------------------------------------------------------- */
  398.  
  399. static _kernel_oserror *_EditSky_redrawcaret(int window_handle, int caret_position)
  400. {
  401.   return wimp_force_redraw(window_handle, 0, -WIN_HEIGHT + caret_position*(COL_HEIGHT+GAP), WIN_WIDTH, -WIN_HEIGHT + caret_position*(COL_HEIGHT+GAP) + GAP);
  402. }
  403.  
  404. /* ----------------------------------------------------------------------- */
  405.  
  406. static int _EditSky_keyhandler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  407. {
  408.   /* Handle key short-cuts */
  409.   ViewData *view_data = (ViewData *)handle;
  410.  
  411.   switch(event_code) {
  412.     /* ------------------------------------------- */
  413.     /*           General file operations           */
  414.  
  415.     case KEY_FILEINFO:
  416.       RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, fileinfo_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  417.       return 1; /* claim event */
  418.  
  419.     case KEY_CLOSEFILE:
  420.       if(null_polling && drag_source == view_data) {
  421.         /* Stop drag before closing window */
  422.         RE(wimp_drag_box((WimpDragBox *)-1));
  423.         _EditSky_dragstopped();
  424.       }
  425.  
  426.       if(view_data->changed_since_save) {
  427.         /* Wait for response */
  428.         dcs_openparent = false;
  429.         RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, dcs_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  430.       }
  431.       else
  432.         RE(toolbox_delete_object(0, id_block->self_id))
  433.       return 1; /* claim event */
  434.  
  435.     case KEY_SAVEFILE:
  436.       /* Open savebox */
  437.       RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, savebox_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  438.       return 1; /* claim event */
  439.  
  440.     case KEY_SELALL:
  441.       EditSky_changeselection(view_data, 0, 62);
  442.       //goto update_menu_if_child;
  443.       return 1; /* claim event */
  444.  
  445.     case KEY_CLEARSEL:
  446.       EditSky_clearselection(view_data);
  447.       //goto update_menu_if_child;
  448.       return 1; /* claim event */
  449.  
  450.     case KEY_PREVIEW:  /* Show interactive preview */
  451.       EditSky_preview(view_data);
  452.       return 1; /* claim event */
  453.  
  454.     case KEY_UP:
  455.       if(view_data->selection_exists) {
  456.         if(view_data->selection_end+1 < 63)
  457.           EditSky_set_caretpos(view_data, view_data->selection_end+2);
  458.         else
  459.           EditSky_set_caretpos(view_data, view_data->selection_end+1);
  460.       } else {
  461.         if(view_data->caret_position < 63)
  462.           EditSky_set_caretpos(view_data, view_data->caret_position+1);
  463.       }
  464.       //RE(_EditSky_scroll(view_data, +(COL_HEIGHT+GAP), 0));
  465.       return 1; /* claim event */
  466.  
  467.     case KEY_DOWN:
  468.       if(view_data->selection_exists) {
  469.         if(view_data->selection_start > 0)
  470.           EditSky_set_caretpos(view_data, view_data->selection_start-1);
  471.         else
  472.           EditSky_set_caretpos(view_data, view_data->selection_start);
  473.       } else {
  474.         if(view_data->caret_position > 0)
  475.           EditSky_set_caretpos(view_data, view_data->caret_position-1);
  476.       }
  477.       //RE(_EditSky_scroll(view_data, -(COL_HEIGHT+GAP), 0));
  478.       return 1; /* claim event */
  479.  
  480.     case KEY_PAGEUP:
  481.       RE(_EditSky_scroll(view_data, +1, 1))
  482.       return 1; /* claim event */
  483.  
  484.     case KEY_PAGEDOWN:
  485.       RE(_EditSky_scroll(view_data, -1, 1))
  486.       return 1; /* claim event */
  487.  
  488.     case KEY_HOME:
  489.       RE(_EditSky_scroll(view_data, +1, 2))
  490.       return 1; /* claim event */
  491.  
  492.     case KEY_END:
  493.       RE(_EditSky_scroll(view_data, -1, 2))
  494.       return 1; /* claim event */
  495.  
  496.     /* ------------------------------------------- */
  497.     /*           Operations on selection           */
  498.  
  499.     case KEY_SMOOTHSEL:
  500.       /* stupidity checks - need at least three */
  501.       if(view_data->selection_exists && ((view_data->selection_end+1) - view_data->selection_start) > 2) {
  502.         smooth_section(&view_data->sky, view_data->selection_start, view_data->selection_end);
  503.         EditSky_rowsupdated(view_data, view_data->selection_start, view_data->selection_end);
  504.         EditSky_markaschanged(view_data);
  505.       }
  506.       else
  507.         _kernel_oswrch(7); /* beep */
  508.       return 1; /* claim event */
  509.  
  510.     case KEY_EDITCOLS:
  511.       /* stupidity checks */
  512.       if(view_data->selection_exists) {
  513.         if(!E(Pal256_set_colour(pal256_sharedid, get_shade(&view_data->sky, view_data->selection_start))))
  514.           RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, pal256_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  515.       }
  516.       else
  517.         _kernel_oswrch(7); /* beep */
  518.       return 1; /* claim event */
  519.  
  520.     case KEY_COPY:
  521.       if(!view_data->selection_exists) {
  522.         _kernel_oswrch(7); /* beep */
  523.         return 1; /* claim event */
  524.       }
  525.       copy_to_clipboard(&view_data->sky, view_data->selection_start, view_data->selection_end);
  526.       //goto update_menu_if_child; /* ungrey paste option */
  527.       return 1; /* claim event */
  528.  
  529.     case KEY_CUT:
  530.       if(!view_data->selection_exists) {
  531.         _kernel_oswrch(7); /* beep */
  532.         return 1; /* claim event */
  533.       }
  534.       if(copy_to_clipboard(&view_data->sky, view_data->selection_start, view_data->selection_end))
  535.         EditSky_remove_sel(view_data);
  536.       //goto update_menu_if_child; /* ungrey paste option (cut) and/or grey selection options (delete & cut) */
  537.       return 1; /* claim event */
  538.  
  539.     case KEY_DELETE:
  540.       if(!view_data->selection_exists) {
  541.         _kernel_oswrch(7); /* beep */
  542.         return 1; /* claim event */
  543.       }
  544.       EditSky_remove_sel(view_data);
  545.       //goto update_menu_if_child; /* ungrey paste option (cut) and/or grey selection options (delete & cut) */
  546.       return 1; /* claim event */
  547.  
  548.     case KEY_INTERPOLATE:
  549.       /* stupidity checks - need at least two */
  550.       if(view_data->selection_exists && ((view_data->selection_end+1) - view_data->selection_start) > 1) {
  551.         RE(wimp_create_menu(CloseMenu,0,0))
  552.         RE(open_topleftofwin(0, Interpolate_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId));
  553.       }
  554.       else
  555.         _kernel_oswrch(7); /* beep */
  556.       return 1; /* claim event */
  557.  
  558.     /* ------------------------------------------- */
  559.     /*           Operations at caret               */
  560.  
  561.     case KEY_PASTE:
  562.       EditSky_paste(view_data);
  563.       //goto update_menu_if_child;
  564.       return 1; /* claim event */
  565.  
  566.     case KEY_INSERT:
  567.       /* stupidity checks - must have caret as insertion point */
  568.       if(!view_data->selection_exists) {
  569.         RE(wimp_create_menu(CloseMenu,0,0))
  570.         RE(open_topleftofwin(0, Insert_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId));
  571.       }
  572.       else
  573.         _kernel_oswrch(7); /* beep */
  574.       return 1; /* claim event */
  575.  
  576.     case KEY_TRAP:
  577.       view_data->trap_caret = !view_data->trap_caret;
  578.       def_trap_caret = view_data->trap_caret;
  579.       //{
  580.       //  ObjectId menus_ancestor;
  581.       //  if(!E(toolbox_get_ancestor(0, SkyMenu_sharedid, &menus_ancestor, NULL)) && menus_ancestor == id_block->self_id)
  582.       //    SkyMenu_update(view_data); /* tick/untick menu item */
  583.       //}
  584.       return 1; /* claim event */
  585.  
  586.     default:
  587.       return 0; /* not interested */
  588.   }
  589.   /* Update of open menus is dodgy, so this doesn't really work... */
  590. //update_menu_if_child:
  591.   //{
  592.   //  ObjectId menus_ancestor;
  593.   //  if(!E(toolbox_get_ancestor(0, SkyMenu_sharedid, &menus_ancestor, NULL)) && menus_ancestor == id_block->self_id) {
  594.   //    EditMenu_update(view_data);
  595.   //    ColsMenu_update(view_data);
  596.   //  }
  597.   //}
  598.   //return 1; /* claim event */
  599. }
  600.  
  601. /* ======================== Wimp event handlers ========================== */
  602.  
  603. static int _EditSky_escwatch(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  604. {
  605.   /* Check whether ESC pressed */
  606.   _kernel_oserror *err;
  607.   int esc;
  608.  
  609.   esc = _kernel_osbyte(129, (112^0xff), 0xff);
  610.   if(esc == _kernel_ERROR) {
  611.     err = _kernel_last_oserror();
  612.     err_complain(err->errnum, err->errmess);
  613.   }
  614.  
  615.   if((esc & 0xff) == 0xff) {
  616.     /* STOP THAT DRAG NOW!!! */
  617.     RE(wimp_drag_box((WimpDragBox *)-1));
  618.     _EditSky_dragstopped();
  619.   }
  620.   return 0; /* event not handled */
  621. }
  622.  
  623. /* ----------------------------------------------------------------------- */
  624.  
  625. static int _EditSky_caretwatch(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  626. {
  627.   /* Keep track of whether this view has the input focus */
  628.   ViewData *view_data = (ViewData *)handle;
  629.   int window_handle;
  630.  
  631.   E_RETV(window_get_wimp_handle(0, id_block->self_id, &window_handle), 0);
  632.  
  633.   switch(event_code) {
  634.     case Wimp_ELoseCaret:
  635.       view_data->has_input_focus = false;
  636.       RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  637.       return 1; /* claim event */
  638.  
  639.     case Wimp_EGainCaret:
  640.       view_data->has_input_focus = true;
  641.       RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  642.       return 1; /* claim event */
  643.  
  644.     default:
  645.       return 0; /* don't handle this event */
  646.   }
  647. }
  648.  
  649. /* ----------------------------------------------------------------------- */
  650.  
  651. static int _EditSky_dragended(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  652. {
  653.   ViewData *dest_data = (ViewData *)handle;
  654.  
  655.   _EditSky_dragstopped();
  656.   if(dragging_bands != DRAGGING_AREA || ghost_caret_win == -1)
  657.     return 1; /* so what? */
  658.  
  659.   /* Area drag dropped - check to see if drag ended on this view */
  660.   int dest_win_handle;
  661.   E_RETV(window_get_wimp_handle(0, dest_data->window_id, &dest_win_handle), 0)
  662.  
  663.   WimpGetPointerInfoBlock pointer;
  664.   E_RETV(wimp_get_pointer_info(&pointer), 0)
  665.   if(pointer.window_handle != dest_win_handle || !drag_source->selection_exists)
  666.     return 0; /* event not handled */
  667.  
  668.   if(dest_data == drag_source && ghost_caret_position > drag_source->selection_start && ghost_caret_position < (drag_source->selection_end+1))
  669.     return 1; /* Can't copy block into self */
  670.  
  671.   int length_to_copy = drag_source->selection_end - drag_source->selection_start + 1;
  672.  
  673.   /* Must buffer data or source block may move with open_gap */
  674.   char *tempstore;
  675.   if(!flex_alloc((flex_ptr)&tempstore, length_to_copy))
  676.     RG_RETV("NoMem", 1) /* claim event */
  677.  
  678.   /* Copy from selection to buffer */
  679.   for(int band = 0; band < length_to_copy; band++)
  680.     tempstore[band] = get_shade(&drag_source->sky, drag_source->selection_start + band);
  681.  
  682.   /* Should we remove source data? */
  683.   bool move_not_copy;
  684.   if((!dragging_shift && drag_source == dest_data)
  685.   || (dragging_shift && drag_source != dest_data))
  686.     move_not_copy = true;
  687.   else
  688.     move_not_copy = false;
  689.  
  690.   /* ====== NOW STUFF STARTS SHIFTING AROUND! ======= */
  691.   if(move_not_copy) {
  692.     if(dest_data == drag_source && ghost_caret_position > drag_source->selection_end)
  693.       ghost_caret_position -= (drag_source->selection_end+1) - drag_source->selection_start;
  694.  
  695.     /* Remove source data */
  696.     EditSky_remove_sel(drag_source);
  697.   }
  698.  
  699.   /* If dropped on selection in destination view then replace */
  700.   if(dest_data != drag_source && dest_data->selection_exists && ghost_caret_position > dest_data->selection_start && ghost_caret_position < (dest_data->selection_end+1)) {
  701.     remove_bands(&dest_data->sky, dest_data->selection_start, dest_data->selection_end);
  702.     ghost_caret_position = dest_data->selection_start;
  703.   }
  704.  
  705.   /* Copy from buffer to ghost caret position */
  706.   open_gap(&dest_data->sky, ghost_caret_position, length_to_copy);
  707.   for(int band = 0; band < length_to_copy && (ghost_caret_position+band) < 63; band++)
  708.     write_shade(&dest_data->sky, ghost_caret_position+band, tempstore[band]);
  709.   flex_free((flex_ptr)&tempstore); /* free temporary buffer */
  710.   EditSky_rowsupdated(dest_data, ghost_caret_position, 62);
  711.  
  712.   if(move_not_copy || dest_data != drag_source) {
  713.     /* Moved within view or moved/copied to diff view
  714.        - Select destination data */
  715.     if(dest_data->selection_exists)
  716.       /* Wipe current selection */
  717.       RE(_EditSky_redrawrows(dest_win_handle, dest_data->selection_start, dest_data->selection_end))
  718.     else
  719.       /* Wipe current caret */
  720.       RE(_EditSky_redrawcaret(dest_win_handle, dest_data->caret_position))
  721.  
  722.     dest_data->selection_start = ghost_caret_position;
  723.     dest_data->selection_end = ghost_caret_position + length_to_copy - 1;
  724.     if(dest_data->selection_end > 62)
  725.       dest_data->selection_end = 62;
  726.     if(ghost_caret_position > 62) {
  727.       dest_data->selection_exists = false;
  728.       dest_data->caret_position = ghost_caret_position;
  729.       RE(_EditSky_redrawcaret(dest_win_handle, ghost_caret_position))
  730.     }
  731.     else
  732.      dest_data->selection_exists = true;
  733.      /* has already been redrawn */
  734.   }
  735.   else {
  736.     /* Copied within same view
  737.     - e.g. still source selected but may have moved */
  738.     if(drag_source->selection_exists) {
  739.       /* If copied above selection it stays put */
  740.       if(drag_source->selection_start > ghost_caret_position) {
  741.         /* Have copied below - budge the selection up */
  742.         drag_source->selection_start += length_to_copy;
  743.         drag_source->selection_end += length_to_copy;
  744.         /* N.B. no need to redraw new selection, as everything above  the insertion point has already been redrawn */
  745.         if(drag_source->selection_end > 62)
  746.           drag_source->selection_end = 62;
  747.         if(drag_source->selection_start > 62) {
  748.           drag_source->selection_exists = false;
  749.           drag_source->caret_position = drag_source->selection_start;
  750.           RE(_EditSky_redrawcaret(dest_win_handle, drag_source->caret_position))
  751.         }
  752.       }
  753.     }
  754.   }
  755.  
  756.   EditSky_markaschanged(dest_data);
  757.   return 1; /* claim event */
  758. }
  759.  
  760. /* ----------------------------------------------------------------------- */
  761.  
  762. static int _EditSky_dragscroll(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  763. {
  764.   /* Handles drag-selection, ghost caret and auto-scrolling of view */
  765.   ViewData *view_data = (ViewData *)handle;
  766.  
  767.   /* Check whether pointer is over this window */
  768.   WimpGetPointerInfoBlock pointer;
  769.   E_RETV(wimp_get_pointer_info(&pointer), 0)
  770.   WimpGetWindowStateBlock window;
  771.   E_RETV(window_get_wimp_handle(0, view_data->window_id, &(window.window_handle)), 0)
  772.   E_RETV(wimp_get_window_state(&window), 0)
  773.   if(window.window_handle != pointer.window_handle) {
  774.     view_data->last_scroll = 0; /* halt scroll interval timer */
  775.     return 0; /* event not handled */
  776.   }
  777.  
  778.   int bottom_scry = (window.visible_area.ymax - window.yscroll) - WIN_HEIGHT;
  779.   int new_ghost_pos = (pointer.y - bottom_scry + COL_HEIGHT/2)/(COL_HEIGHT+GAP);
  780.  
  781.   switch(dragging_bands) {
  782.     case DRAGGING_SEL:
  783.       /* Adjust selection */
  784.       {
  785.         int new_start, new_end;
  786.         if(view_data->caret_position < new_ghost_pos) {
  787.           new_start = view_data->caret_position;
  788.           new_end = new_ghost_pos-1;
  789.         }
  790.         else {
  791.           new_start = new_ghost_pos;
  792.           new_end = view_data->caret_position-1;
  793.         }
  794.         if(new_start == new_end+1)
  795.           EditSky_set_caretpos(view_data, new_start);
  796.         else
  797.           EditSky_changeselection(view_data, new_start, new_end);
  798.       }
  799.       break;
  800.  
  801.     case DRAGGING_AREA:
  802.       /* Update ghost caret (insertion point) */
  803.       if(ghost_caret_win != window.window_handle || ghost_caret_position != new_ghost_pos) {
  804.         /* Undraw ghost caret (may be in another window) */
  805.         if(ghost_caret_win != -1) {
  806.           if(!ghost_caret_view->selection_exists || ghost_caret_position > ghost_caret_view->selection_end || ghost_caret_position <= ghost_caret_view->selection_start)
  807.             RE(_EditSky_redrawcaret(ghost_caret_win, ghost_caret_position))
  808.         }
  809.  
  810.         /* Draw ghost caret */
  811.         ghost_caret_position = new_ghost_pos;
  812.         ghost_caret_win = window.window_handle;
  813.         ghost_caret_view = view_data;
  814.         if(!view_data->selection_exists || ghost_caret_position > view_data->selection_end || ghost_caret_position <= view_data->selection_start)
  815.           RE(_EditSky_redrawcaret(window.window_handle, ghost_caret_position))
  816.       }
  817.       break;
  818.   }
  819.  
  820.   /* Check whether auto-scrolling is needed */
  821.   if(pointer.x < window.visible_area.xmin || pointer.x >= window.visible_area.xmax)
  822.     return 0; /* event not handled */
  823.  
  824.   {
  825.     int scroll_diff = 0, border;
  826.  
  827.     /* Cope with very narrow views where borders would overlap */
  828.     if(window.visible_area.ymax - window.visible_area.ymin < SCROLL_BORDER*2)
  829.       border = (window.visible_area.ymax - window.visible_area.ymin)/2;
  830.     else
  831.       border = SCROLL_BORDER;
  832.  
  833.     {
  834.       int scroll_start = (window.visible_area.ymax - PANE_HEIGHT - (2<<y_eigen)) - border;
  835.       if(pointer.y > scroll_start && pointer.y < window.visible_area.ymax)
  836.         scroll_diff = pointer.y - scroll_start;
  837.       else {
  838.         scroll_start = window.visible_area.ymin + border;
  839.         if(pointer.y >= window.visible_area.ymin && pointer.y < scroll_start)
  840.           scroll_diff = -(scroll_start - pointer.y);
  841.       }
  842. #ifndef NDEBUG
  843.       {char string[255];sprintf(string,"report scroll_start = %d, scroll_diff = %d", scroll_start, scroll_diff);_kernel_oscli(string);}
  844. #endif
  845.     }
  846.     if(scroll_diff != 0) {
  847.       clock_t new_time = clock();
  848.  
  849.       if(view_data->last_scroll != 0) {
  850.         /* Scroll window by amount based on elapsed time */
  851.         clock_t time_diff;
  852.  
  853.         if(new_time < view_data->last_scroll)
  854.           time_diff = new_time + (UINT_MAX - view_data->last_scroll); /* timer has wrapped! */
  855.         else
  856.           time_diff = new_time - view_data->last_scroll;
  857.  
  858.         /* Put a cap on enormous time intervals */
  859.         if(time_diff > 25)
  860.           time_diff = 25;
  861.  
  862.         /* Re-open window with modified scroll offsets */
  863.         window.yscroll += (scroll_diff * (signed int)time_diff * 8) / border;
  864.         RE(wimp_open_window((WimpOpenWindowBlock *)&window));
  865.       }
  866.  
  867.       /* Store new time */
  868.       if(new_time == 0)
  869.         view_data->last_scroll = 1; /* 0 is used as a special indicator */
  870.       else
  871.         view_data->last_scroll = new_time;
  872.     } else
  873.       view_data->last_scroll = 0; /* have gone outside scroll area */
  874.   }
  875.   return 1; /* claim event */
  876. }
  877.  
  878. /* ----------------------------------------------------------------------- */
  879.  
  880. static int _EditSky_redrawhandler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  881. {
  882.   /* Custom redraw for editing window */
  883.   ViewData *view_data = (ViewData *)handle;
  884.   WimpRedrawWindowBlock block;
  885.   int more;
  886.   int bottom_scry, left_scrx;
  887.   char num_as_text[4];
  888.   WimpPlotIconBlock ploticonblock;
  889.  
  890.   block.window_handle = ((WimpRedrawWindowRequestEvent *)event)->window_handle;
  891.  
  892.   /* Set common values of icons */
  893.   ploticonblock.bbox.xmin = 8;
  894.   ploticonblock.bbox.xmax = WIN_WIDTH-8;
  895.   ploticonblock.data.it.buffer_size = 4;
  896.  
  897.   E_RETV(wimp_redraw_window(&block, &more), 1)
  898.   while (more) {
  899.     /* Calculate work area origin in screen coordinates */
  900.     left_scrx = block.visible_area.xmin - block.xscroll;
  901.     bottom_scry = block.visible_area.ymax - block.yscroll - WIN_HEIGHT;
  902.  
  903.     /* Which rows should be drawn? */
  904.     int min_row = (block.redraw_area.ymin - bottom_scry) / (COL_HEIGHT+GAP);
  905.     int max_row = (block.redraw_area.ymax - bottom_scry) / (COL_HEIGHT+GAP);
  906.  
  907.     /* Plot selection */
  908.     if(view_data->selection_exists && (view_data->selection_end+1 >= min_row && view_data->selection_start <= max_row)) {
  909.     /* N.B. the +1 is because a small part of the selection rectangle is actually plotted on the row above */
  910.       RE(wimp_set_colour(4))
  911.       RE(_swix(OS_Plot, _INR(0,2),
  912.         4, /* Move, absolute */
  913.         left_scrx,
  914.         bottom_scry + view_data->selection_start*(COL_HEIGHT+GAP) + GAP/2
  915.       ))
  916.       RE(_swix(OS_Plot, _INR(0,2),
  917.         96+5, /* Rect, absolute */
  918.         left_scrx + WIN_WIDTH - 1,
  919.         bottom_scry + (view_data->selection_end+1)*(COL_HEIGHT+GAP) + GAP/2 - 1
  920.       ))
  921.     }
  922.  
  923.     /* Plot sky shades */
  924.     for(int row = min_row;row <= max_row;row++) {
  925.       /* We don't have data for an infinite number of bands... */
  926.       if(row > -1 && row < 63) {
  927.         /* Plot colour band */
  928.         ploticonblock.bbox.ymin = -WIN_HEIGHT + (row*(COL_HEIGHT+GAP)) + GAP;
  929.         ploticonblock.bbox.ymax = ploticonblock.bbox.ymin + COL_HEIGHT;
  930.         ploticonblock.flags = WimpIcon_Text
  931.                             | WimpIcon_Indirected
  932.                             | WimpIcon_HCentred
  933.                             | WimpIcon_VCentred
  934.                             | WimpIcon_Filled
  935.                             | (WimpIcon_FGColour * view_data->icon_FGcols[row]);
  936.         if(view_data->selection_exists && row >= view_data->selection_start && row <= view_data->selection_end) {
  937.           ploticonblock.flags |= WimpIcon_Border;
  938.           sprintf(num_as_text, "%d", row);
  939.         }
  940.         else
  941.           *num_as_text = 0;
  942.         ploticonblock.data.it.buffer = num_as_text;
  943.         ploticonblock.data.it.validation = (char *)(view_data->icon_validations[row]);
  944.         RE(wimp_plot_icon(&ploticonblock))
  945.       }
  946.  
  947.       /* Plot caret */
  948.       if(view_data->caret_position == row && !view_data->selection_exists) {
  949.         int col;
  950.         if(view_data->has_input_focus)
  951.           col = 11; /* red */
  952.         else
  953.           col = 3; /* grey */
  954.         RE(_EditSky_plotcursor(left_scrx+4, bottom_scry+(row*(COL_HEIGHT+GAP))+8, col))
  955.       }
  956.  
  957.       /* Plot ghost caret */
  958.       if(null_polling && dragging_bands == DRAGGING_AREA && block.window_handle == ghost_caret_win && ghost_caret_position == row) {
  959.          if(!view_data->selection_exists || ghost_caret_position > view_data->selection_end || ghost_caret_position <= view_data->selection_start) {
  960.            RE(_EditSky_plotcursor(left_scrx+4, bottom_scry+(row*(COL_HEIGHT+GAP))+8, 14)) /* orange */
  961.          }
  962.       }
  963.     }
  964.  
  965.     /* Get next redraw rectangle */
  966.     E_RETV(wimp_get_rectangle(&block, &more), 1)
  967.   }
  968.   return 1; /* claim event */
  969. }
  970.  
  971. /* ----------------------------------------------------------------------- */
  972.  
  973. static _kernel_oserror *_EditSky_scroll(ViewData *view_data, int scrollmod, int page)
  974. {
  975.   /* Vertically scroll our editing window */
  976.   WimpGetWindowInfoBlock window;
  977.  
  978.   THROW(window_get_wimp_handle(0, view_data->window_id, &(window.window_handle)))
  979.   THROW(wimp_get_window_info_no_icon_data(&window))
  980.   switch(page) {
  981.     case 0:
  982.       window.window_data.yscroll += scrollmod;
  983.       break;
  984.  
  985.     case 1:
  986.       window.window_data.yscroll += scrollmod * ((window.window_data.visible_area.ymax - PANE_HEIGHT-(1<<y_eigen)) - window.window_data.visible_area.ymin);
  987.       break;
  988.  
  989.     case 2:
  990.       if(scrollmod > 0)
  991.         window.window_data.yscroll = 0;
  992.       else
  993.         if(scrollmod < 0)
  994.           window.window_data.yscroll = window.window_data.extent.ymin + ((window.window_data.visible_area.ymax - PANE_HEIGHT-(1<<y_eigen)) - window.window_data.visible_area.ymin);
  995.       break;
  996.   }
  997.   THROW(wimp_open_window((WimpOpenWindowBlock *)&window))
  998.   if(view_data->trap_caret)
  999.     return scroll_caret_for_win(view_data, (WimpOpenWindowBlock *)&window);
  1000.   else
  1001.     return NULL;
  1002. }
  1003.  
  1004. /* ----------------------------------------------------------------------- */
  1005.  
  1006. static _kernel_oserror *_EditSky_plotcursor(int x, int y, int col)
  1007. {
  1008.   /* Plot cursor */
  1009.   THROW(wimp_set_colour(col))
  1010.   THROW(_swix(OS_Plot, _INR(0,2), 4,          x, y+1)) /* Move, absolute */
  1011.   THROW(_swix(OS_Plot, _INR(0,2), 96+1,     540,  -2)) /* Rect, relative */
  1012.   THROW(_swix(OS_Plot, _INR(0,2), 4,    x+540-1, y-8)) /* Move, absolute */
  1013.   THROW(_swix(OS_Plot, _INR(0,2), 96+1,      +2,  14)) /* Rect, relative */
  1014.   THROW(_swix(OS_Plot, _INR(0,2), 4,        x-1, y-8)) /* Move, absolute */
  1015.   return _swix(OS_Plot, _INR(0,2), 96+1,     +2,  14); /* Rect, relative */
  1016. }
  1017.  
  1018. /* ----------------------------------------------------------------------- */
  1019.  
  1020. static int _EditSky_opening(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  1021. {
  1022.   ViewData *view_data = (ViewData *)handle;
  1023.  
  1024.   if(view_data->trap_caret)
  1025.     RE(scroll_caret_for_win(view_data, &event->open_window_request))
  1026.  
  1027.   ObjectId parent = NULL_ObjectId;
  1028.   ComponentId parent_component = NULL_ComponentId;
  1029.   RE(toolbox_get_parent(0, id_block->self_id, &parent, &parent_component))
  1030.   RE(toolbox_show_object(0, id_block->self_id, Toolbox_ShowObject_FullSpec, &event->open_window_request.visible_area, parent, parent_component))
  1031.  
  1032.   return 1; /* claim event */
  1033. }
  1034.  
  1035. /* ----------------------------------------------------------------------- */
  1036.  
  1037. static int _EditSky_closing(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  1038. {
  1039.   WimpGetPointerInfoBlock ptr;
  1040.   ViewData *view_data = (ViewData *)handle;
  1041.   int shift_held;
  1042.   bool parent_flag;
  1043.  
  1044.   /* Check for ADJUST-click on close icon */
  1045.   if(!E(wimp_get_pointer_info(&ptr)) && FLAG_SET(ptr.button_state, Wimp_MouseButtonAdjust)) {
  1046.  
  1047.     /* Is Shift key pressed? */
  1048.     shift_held = _kernel_osbyte(129, (0^0xff), 0xff);
  1049.     if(shift_held == _kernel_ERROR) {
  1050.       err_check_rep(_kernel_last_oserror());
  1051.       return 1; /* claim event */
  1052.     }
  1053.     if((shift_held & 0xff) == 0xff) {
  1054.       /* Shift-ADJUST: open parent directory and don't attempt to close window */
  1055.       EditSky_openparentdir(view_data);
  1056.       return 1; /* claim event */
  1057.     }
  1058.     /* ADJUST click with no shift: Open parent and attempt to close window */
  1059.     parent_flag = true;
  1060.   }
  1061.   else
  1062.     parent_flag = false; /* no ADJUST click */
  1063.  
  1064.   /* Attempt to close window */
  1065.   if(view_data->changed_since_save) {
  1066.     /* Ask them whether to save or discard changes */
  1067.     dcs_openparent = parent_flag;
  1068.     RE(open_topleftofwin(Toolbox_ShowObject_AsMenu, dcs_sharedid, id_block->self_id, id_block->self_id, NULL_ComponentId))
  1069.   }
  1070.   else {
  1071.     /* No unsaved changes */
  1072.     if(parent_flag)
  1073.       EditSky_openparentdir(view_data); /* Open parent directory */
  1074.  
  1075.     /* The delete handler will do the rest... */
  1076.     RE(toolbox_delete_object(0, id_block->self_id))
  1077.   }
  1078.   return 1; /* claim event */
  1079. }
  1080.  
  1081. /* ----------------------------------------------------------------------- */
  1082.  
  1083. void EditSky_set_caretpos(ViewData *view_data, int new_pos)
  1084. {
  1085.   assert(new_pos >= 0 && new_pos <= 63);
  1086.  
  1087.   int window_handle;
  1088.   E_RET(window_get_wimp_handle(0, view_data->window_id, &window_handle))
  1089.  
  1090.   /* Clear any selection */
  1091.   if(view_data->selection_exists) {
  1092.     view_data->selection_exists = false;
  1093.     RE(_EditSky_redrawrows(window_handle, view_data->selection_start, view_data->selection_end))
  1094.   } else {
  1095.     /* if no selection existed (e.g. caret), wipe old caret */
  1096.     if(view_data->caret_position == new_pos)
  1097.       return; /* Do nothing! */
  1098.     RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  1099.   }
  1100.  
  1101.   if(!view_data->selection_exists || new_pos <= view_data->selection_start || new_pos > view_data->selection_end)
  1102.     /* if not within area of de-selection redraw, draw new caret */
  1103.     RE(_EditSky_redrawcaret(window_handle, new_pos))
  1104.   view_data->caret_position = new_pos;
  1105.  
  1106.   RE(scroll_win_for_caret(view_data, window_handle))
  1107. }
  1108.  
  1109. /* ----------------------------------------------------------------------- */
  1110.  
  1111. static int _EditSky_clickhandler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle)
  1112. {
  1113.   /* In order that the pseudo-transient dbox mechanism can work
  1114.      we pass mouse click events on rather than claiming them */
  1115.   WimpMouseClickEvent *wmce = (WimpMouseClickEvent *)event;
  1116.   ViewData *view_data = (ViewData *)handle;
  1117.  
  1118.   /* bypass this for drags, to speed things up */
  1119.   if(wmce->buttons == Wimp_MouseButtonSelect*256 || wmce->buttons == Wimp_MouseButtonAdjust*256) {
  1120.     /* Give our window the input focus */
  1121.     if(view_data->has_input_focus == false)
  1122.       RE(wimp_set_caret_position(wmce->window_handle, -1, 0, 0, -1, -1))
  1123.  
  1124.     /* Close any open dialogues */
  1125.     //RE(hide_shared_if_child(id_block->self_id, Interpolate_sharedid))
  1126.     //RE(hide_shared_if_child(id_block->self_id, Insert_sharedid))
  1127.     //This is now done globally
  1128.   }
  1129.  
  1130.   if(wmce->buttons == Wimp_MouseButtonMenu)
  1131.     return 0; /* event not handled */
  1132.  
  1133.   /* what row was clicked? */
  1134.   WimpGetWindowStateBlock getwincoords;
  1135.   getwincoords.window_handle = wmce->window_handle;
  1136.   E_RETV(wimp_get_window_state(&getwincoords), 0)
  1137.   int bottom_scry = (getwincoords.visible_area.ymax - getwincoords.yscroll) - WIN_HEIGHT;
  1138.   int click_pos = (wmce->mouse_y - bottom_scry + COL_HEIGHT/2)/(COL_HEIGHT+GAP);
  1139.   int selstart_scry = bottom_scry + (view_data->selection_start*(COL_HEIGHT+GAP)) + GAP;
  1140.   int selend_scry = bottom_scry + ((view_data->selection_end+1)*(COL_HEIGHT+GAP));
  1141.  
  1142.   switch(wmce->buttons) {
  1143.     case Wimp_MouseButtonSelect*256: /* SELECT click */
  1144.       if(!view_data->selection_exists || wmce->mouse_y < selstart_scry || wmce->mouse_y > selend_scry)
  1145.         EditSky_set_caretpos(view_data, click_pos);
  1146.       break;
  1147.  
  1148.     case Wimp_MouseButtonAdjust*256: /* ADJUST click */
  1149.       if(view_data->selection_exists) {
  1150.         /* Adjust nearest end of contiguous selection */
  1151.         int new_start = view_data->selection_start;
  1152.         int new_fin = view_data->selection_end;
  1153.         if(absdiff(click_pos, new_start) < absdiff(click_pos-1, new_fin))
  1154.           new_start = click_pos;
  1155.         else
  1156.           new_fin = click_pos-1;
  1157.  
  1158.         if(new_start == (new_fin+1))
  1159.           EditSky_set_caretpos(view_data, new_start);
  1160.         else
  1161.           EditSky_changeselection(view_data, new_start, new_fin);
  1162.       }
  1163.       else {
  1164.         /* Create contiguous selection from caret to here */
  1165.         if(click_pos != view_data->caret_position) {
  1166.           if(click_pos < view_data->caret_position)
  1167.             EditSky_changeselection(view_data, click_pos, view_data->caret_position-1);
  1168.           else
  1169.             EditSky_changeselection(view_data, view_data->caret_position, click_pos-1);
  1170.         }
  1171.       }
  1172.       break;
  1173.  
  1174.     case Wimp_MouseButtonSelect*16: /* SELECT or ADJUST drag */
  1175.     case Wimp_MouseButtonAdjust*16:
  1176.       if(wmce->buttons != Wimp_MouseButtonAdjust*16
  1177.       && view_data->selection_exists
  1178.       && wmce->mouse_y > selstart_scry
  1179.       && wmce->mouse_y < selend_scry) {
  1180.         /* Drag current selection */
  1181.         WimpDragBox dragbox;
  1182.         dragbox.drag_type = Wimp_DragBox_DragFixedDash;
  1183.  
  1184.         /* initial estimate of bounding box is for entire selection */
  1185.         {
  1186.           int left_scrx = getwincoords.visible_area.xmin - getwincoords.xscroll;
  1187.           dragbox.dragging_box.xmin = left_scrx+8;
  1188.           dragbox.dragging_box.xmax = left_scrx+WIN_WIDTH-8;
  1189.         }
  1190.         dragbox.dragging_box.ymin = bottom_scry + (view_data->selection_start*(COL_HEIGHT+GAP)) + GAP;
  1191.         dragbox.dragging_box.ymax = bottom_scry + (view_data->selection_end*(COL_HEIGHT+GAP)) + GAP + COL_HEIGHT;
  1192.  
  1193.         /* clip bounding box to visible area of window */
  1194.         if(dragbox.dragging_box.ymin < getwincoords.visible_area.ymin)
  1195.           dragbox.dragging_box.ymin = getwincoords.visible_area.ymin;
  1196.         if(dragbox.dragging_box.ymax > getwincoords.visible_area.ymax - PANE_HEIGHT-(2<<y_eigen))
  1197.           dragbox.dragging_box.ymax = getwincoords.visible_area.ymax - PANE_HEIGHT-(2<y_eigen);
  1198.  
  1199.         /* allow drag anywhere on the screen */
  1200.         dragbox.parent_box.xmin = 0 - (wmce->mouse_x - dragbox.dragging_box.xmin);
  1201.         dragbox.parent_box.ymin = 0 - (wmce->mouse_y - dragbox.dragging_box.ymin);
  1202.         dragbox.parent_box.xmax = (x_windlimit<<x_eigen) + (dragbox.dragging_box.xmax - wmce->mouse_x);
  1203.         dragbox.parent_box.ymax = (y_windlimit<<y_eigen) + (dragbox.dragging_box.ymax - wmce->mouse_y);
  1204.         RE(wimp_drag_box(&dragbox))
  1205.  
  1206.         ghost_caret_win = -1;
  1207.         dragging_bands = DRAGGING_AREA;
  1208.  
  1209.         /* Check whether SHIFT held */
  1210.         {
  1211.           int keypressed = _kernel_osbyte(129, (0^0xff), 0xff);
  1212.           if(keypressed == _kernel_ERROR) {
  1213.             _kernel_oserror *err = _kernel_last_oserror();
  1214.             err_complain(err->errnum, err->errmess);
  1215.           }
  1216.           dragging_shift = (keypressed & 0xff) == 0xff;
  1217.         }
  1218.  
  1219.         /* Listen for ESCAPE pressed to abort drag
  1220.            (should get first bite at cherry as registered after scroll/ghost handlers) */
  1221.         RE(event_register_wimp_handler(-1, Wimp_ENull, _EditSky_escwatch, NULL))
  1222.       }
  1223.       else {
  1224.         /* Start new selection or drag near end of existing selection */
  1225.  
  1226.         if(wmce->buttons == Wimp_MouseButtonAdjust*16 && view_data->selection_exists) {
  1227.           /* Set 'caret' (fixed endpoint) to far end of selection */
  1228.           if(absdiff(click_pos, view_data->selection_start) < absdiff(click_pos-1, view_data->selection_end))
  1229.             view_data->caret_position = view_data->selection_end+1;
  1230.           else
  1231.             view_data->caret_position = view_data->selection_start;
  1232.         }
  1233.         dragging_bands = DRAGGING_SEL;
  1234.  
  1235.         WimpDragBox dragbox;
  1236.         dragbox.drag_type = Wimp_DragBox_DragPoint;
  1237.         dragbox.parent_box.ymax = getwincoords.visible_area.ymax - PANE_HEIGHT - (2<<y_eigen);
  1238.         dragbox.parent_box.xmax = getwincoords.visible_area.xmax - (1<<x_eigen);
  1239.         dragbox.parent_box.ymin = getwincoords.visible_area.ymin;
  1240.         dragbox.parent_box.xmin = getwincoords.visible_area.xmin;
  1241.         RE(wimp_drag_box(&dragbox))
  1242.       }
  1243.  
  1244.       /* Watch out for auto-scrolling */
  1245.       nullpoll_register();
  1246.       view_data->last_scroll = 0; /* reset scroll interval timer */
  1247.       null_polling = true;
  1248.       drag_source = view_data;
  1249.       break;
  1250.  
  1251.     case Wimp_MouseButtonSelect: /* SELECT double-click */
  1252.       if(view_data->selection_exists && wmce->mouse_y > selstart_scry && wmce->mouse_y < selend_scry) {
  1253.         /* Change the colour of the selected bands */
  1254.         if(!E(Pal256_set_colour(pal256_sharedid, get_shade(&view_data->sky, (wmce->mouse_y - bottom_scry)/(COL_HEIGHT+GAP)))))
  1255.           RE(toolbox_show_object(Toolbox_ShowObject_AsMenu, pal256_sharedid, Toolbox_ShowObject_AtPointer, NULL, id_block->self_id, NULL_ComponentId))
  1256.       }
  1257.       break;
  1258.   }
  1259.   return 0; /* pass event on */
  1260. }
  1261.  
  1262. /* ======================== Toolbox event handlers ======================= */
  1263.  
  1264. static int _EditSky_colourselhandler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  1265. {
  1266.   /* Colour selected - fill selected skyfile bands */
  1267.   ViewData *view_data = (ViewData *)handle;
  1268.  
  1269.   if(id_block->parent_id != view_data->window_id)
  1270.     return 0; /* none of our business */
  1271.  
  1272.   plain_overwrite(&view_data->sky, view_data->selection_start, view_data->selection_end, ((Pal256ColourSelectedEvent *)event)->colour_number);
  1273.   EditSky_rowsupdated(view_data, view_data->selection_start, view_data->selection_end);
  1274.   EditSky_markaschanged(view_data);
  1275.   return 1; /* claim event */
  1276. }
  1277.  
  1278. /* ----------------------------------------------------------------------- */
  1279.  
  1280. static int _EditSky_minskychanged(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  1281. {
  1282.   NumberRangeValueChangedEvent *nrvc = (NumberRangeValueChangedEvent *)event;
  1283.   ViewData *view_data = (ViewData *)handle;
  1284.  
  1285.   if(id_block->self_component != GADGET_MINSKY || nrvc->new_value > 10000 || nrvc->new_value < 0)
  1286.     return 0; /* event not handled */
  1287.   view_data->sky->min_sky_height = nrvc->new_value;
  1288.  
  1289.   /* Re-render sky preview (if any) */
  1290.   if(view_data->preview_id != NULL)
  1291.     Preview_rendersky(view_data->preview_id);
  1292.  
  1293.   /* Mark as unsaved changes */
  1294.   EditSky_markaschanged(view_data);
  1295.   return 1; /* claim event */
  1296. }
  1297.  
  1298. /* ----------------------------------------------------------------------- */
  1299.  
  1300. static int _EditSky_starheightchanged(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  1301. {
  1302.   NumberRangeValueChangedEvent *nrvc = (NumberRangeValueChangedEvent *)event;
  1303.   ViewData *view_data = (ViewData *)handle;
  1304.  
  1305.   if(id_block->self_component != GADGET_MINSTAR || nrvc->new_value > 10000)
  1306.     return 0; /* event not handled */
  1307.   view_data->sky->min_stars_height = nrvc->new_value;
  1308.  
  1309.   /* Re-render sky preview (if any) */
  1310.   if(view_data->preview_id != NULL)
  1311.     Preview_rendersky(view_data->preview_id);
  1312.  
  1313.   /* Mark as unsaved changes */
  1314.   EditSky_markaschanged(view_data);
  1315.   return 1; /* claim event */
  1316. }
  1317.  
  1318. /* ----------------------------------------------------------------------- */
  1319.  
  1320. static int _EditSky_deleted(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle)
  1321. {
  1322.   ViewData *view_data = (ViewData *)handle;
  1323.  
  1324.   /* Delete any associated preview window and close child dialogues */
  1325.   if(view_data->preview_id != NULL)
  1326.     RE(toolbox_delete_object(0, view_data->preview_id))
  1327.   RE(hide_shared_if_child(id_block->self_id, Interpolate_sharedid))
  1328.   RE(hide_shared_if_child(id_block->self_id, Insert_sharedid))
  1329.  
  1330.   /* Remove handlers */
  1331.   RE(event_deregister_toolbox_handler(id_block->self_id, Toolbox_ObjectDeleted, _EditSky_deleted, view_data))
  1332.  
  1333.   RE(event_deregister_toolbox_handler(view_data->dispvalues_pane, NumberRange_ValueChanged, _EditSky_minskychanged, view_data))
  1334.   RE(event_deregister_toolbox_handler(view_data->dispvalues_pane, NumberRange_ValueChanged, _EditSky_starheightchanged, view_data))
  1335.   RE(event_deregister_toolbox_handler(id_block->self_id, -1, _EditSky_keyhandler, view_data))
  1336.   RE(event_deregister_toolbox_handler(pal256_sharedid, Pal256_ColourSelected, _EditSky_colourselhandler, view_data))
  1337.  
  1338.   RE(event_deregister_wimp_handler(id_block->self_id, Wimp_EMouseClick, _EditSky_clickhandler, view_data))
  1339.   RE(event_deregister_wimp_handler(id_block->self_id, Wimp_ECloseWindow, _EditSky_closing, view_data))
  1340.   RE(event_deregister_wimp_handler(id_block->self_id, Wimp_EOpenWindow, _EditSky_opening, view_data))
  1341.   RE(event_deregister_wimp_handler(id_block->self_id, Wimp_ERedrawWindow, _EditSky_redrawhandler, view_data))
  1342.   RE(event_deregister_wimp_handler(-1, Wimp_ENull, _EditSky_dragscroll, view_data))
  1343.   RE(event_deregister_wimp_handler(-1, Wimp_EUserDrag, _EditSky_dragended, view_data))
  1344.   RE(event_deregister_wimp_handler(id_block->self_id, -1, _EditSky_caretwatch, view_data))
  1345.  
  1346.   RE(loader_deregister_listener(FILETYPE_SKYCOLS, id_block->self_id, NULL))
  1347.  
  1348.   RE(ViewsMenu_remove(id_block->self_id))
  1349.  
  1350.   flex_free((flex_ptr)&view_data->sky);
  1351.   flex_free((flex_ptr)&view_data->icon_FGcols);
  1352.   flex_free((flex_ptr)&view_data->icon_validations);
  1353.   free(view_data->last_savepath);
  1354.   free(view_data);
  1355.   return 1; /* claim event */
  1356. }
  1357.  
  1358. /* ======================= Loader finished handler ======================= */
  1359.  
  1360. static void _EditSky_loadfile(char *title, bool data_saved, flex_ptr buffer, int filetype, void *handle)
  1361. {
  1362.   ViewData *view_data = (ViewData *)handle;
  1363.  
  1364.   /* Check file format */
  1365.   if(!verify_sky_file((SF_SkyColours **)buffer)) {
  1366.     flex_free(buffer);
  1367.     return;
  1368.   }
  1369.  
  1370.   int insert = prepare_to_insert(view_data);
  1371.  
  1372.   /* Insert data at cursor */
  1373.   open_gap(&view_data->sky, insert, 62);
  1374.   for(int band = 0; band < 63 && (insert+band) < 63; band++)
  1375.     write_shade(&view_data->sky, insert+band, get_shade((SF_SkyColours **)buffer, band));
  1376.   flex_free(buffer);
  1377.  
  1378.   /* Update displayed colours */
  1379.   view_data->selection_exists = true;
  1380.   view_data->selection_start = insert;
  1381.   view_data->selection_end = 62;
  1382.   EditSky_rowsupdated(view_data, insert, 62);
  1383.  
  1384.   /* Set displayed values from header */
  1385.   RE(numberrange_set_value(0, view_data->dispvalues_pane, GADGET_MINSKY, view_data->sky->min_sky_height))
  1386.   RE(numberrange_set_value(0, view_data->dispvalues_pane, GADGET_MINSTAR, view_data->sky->min_stars_height))
  1387.  
  1388.   /* Mark as unsaved changes, and redraw preview (if any) */
  1389.   EditSky_markaschanged(view_data);
  1390. }
  1391.  
  1392. /* ========================== Utility functions ========================== */
  1393.  
  1394. bool EditSky_newfile(ViewData *view_data, char *title, bool title_is_file)
  1395. {
  1396.   _kernel_osfile_block osfb;
  1397.   char *new_ptr, *views_filepath;
  1398.  
  1399.   new_ptr = realloc(view_data->last_savepath, strlen(title) + 1);
  1400.   if(new_ptr == NULL)
  1401.     RG_RETV("NoMem", false) /* failed */
  1402.   view_data->last_savepath = new_ptr;
  1403.   strcpy(view_data->last_savepath, title); /* not necessarily /strictly/ true but what the hell */
  1404.  
  1405.   if(title_is_file) {
  1406.     /* Get datestamp of file */
  1407.     if(_kernel_osfile(17, title, &osfb) == _kernel_ERROR)
  1408.       E_RETV(_kernel_last_oserror(), false)
  1409.     if((osfb.load & 0xfff00000) == 0xfff00000) {
  1410.       /* File has datestamp and filetype */
  1411.       view_data->file_date[0] = osfb.exec;
  1412.       view_data->file_date[1] = osfb.load;
  1413.     }
  1414.     else {
  1415.       /* File has load & exec addresses */
  1416.       view_data->file_date[0] = 0;
  1417.       view_data->file_date[1] = 0;
  1418.     }
  1419.   }
  1420.   else {
  1421.     /* Get current time & date */
  1422.     view_data->file_date[0] = 3; /* Read soft-copy of CMOS clock as 5-byte integer */
  1423.     if(_kernel_osword(14, view_data->file_date) == _kernel_ERROR)
  1424.       E_RETV(_kernel_last_oserror(), false)
  1425.   }
  1426.  
  1427.   /* Set title of editing window */
  1428.   E_RETV(window_set_title(0, view_data->window_id, title), false)
  1429.   if(title_is_file)
  1430.     views_filepath = title;
  1431.   else
  1432.     views_filepath = "";
  1433.   E_RETV(ViewsMenu_setname(view_data->window_id, title, views_filepath), false)
  1434.  
  1435.   /* Set title of preview window */
  1436.   if(view_data->preview_id != NULL) {
  1437.     E_RETV(window_set_title(0, view_data->preview_id, msgs_lookup_sub1("PrevTitle", tail(title, 1))), false)
  1438.   }
  1439.  
  1440.   view_data->changed_since_save = false; /* mark as unchanged */
  1441.   return true; /* success */
  1442. }
  1443.  
  1444. /* ----------------------------------------------------------------------- */
  1445.  
  1446. void EditSky_markaschanged(ViewData *view_data)
  1447. {
  1448.   char wintitle[256];
  1449.  
  1450.   if(view_data->changed_since_save)
  1451.     return;
  1452.  
  1453.   /* Mark data as changed */
  1454.   sprintf(wintitle, "%.253s *", view_data->last_savepath);
  1455.   RE(window_set_title(0, view_data->window_id, wintitle))
  1456.   RE(ViewsMenu_setname(view_data->window_id, wintitle, NULL))
  1457.   view_data->changed_since_save = true;
  1458. }
  1459.  
  1460. /* ----------------------------------------------------------------------- */
  1461.  
  1462. void EditSky_changeselection(ViewData *view_data, int newsel_start, int newsel_end)
  1463. {
  1464.   /* Find areas to redraw when selection changed */
  1465.   int oldsel_start = view_data->selection_start;
  1466.   int oldsel_end = view_data->selection_end;
  1467.  
  1468.   int window_handle;
  1469.   E_RET(window_get_wimp_handle(0, view_data->window_id, &window_handle))
  1470.  
  1471.   if(!view_data->selection_exists) {
  1472.     /* Undraw caret */
  1473.     RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  1474.  
  1475.     /* Draw new selection */
  1476.     RE(_EditSky_redrawrows(window_handle, newsel_start, newsel_end))
  1477.   }
  1478.   else {
  1479.     if(newsel_end < oldsel_start || oldsel_end < newsel_start) {
  1480.       /* Undraw old selection */
  1481.       RE(_EditSky_redrawrows(window_handle, oldsel_start, oldsel_end))
  1482.  
  1483.       /* Draw new selection */
  1484.       RE(_EditSky_redrawrows(window_handle, newsel_start, newsel_end))
  1485.     }
  1486.     else {
  1487.       /* Calculate shift in end positions */
  1488.       if(oldsel_start < newsel_start)
  1489.         RE(_EditSky_redrawrows(window_handle, oldsel_start, newsel_start-1))
  1490.  
  1491.       if(oldsel_start > newsel_start)
  1492.         RE(_EditSky_redrawrows(window_handle, newsel_start, oldsel_start-1))
  1493.  
  1494.       if(oldsel_end < newsel_end)
  1495.         RE(_EditSky_redrawrows(window_handle, oldsel_end+1, newsel_end))
  1496.  
  1497.       if(oldsel_end > newsel_end)
  1498.         RE(_EditSky_redrawrows(window_handle, newsel_end+1, oldsel_end))
  1499.     }
  1500.   }
  1501.   view_data->selection_exists = true;
  1502.   view_data->selection_start = newsel_start;
  1503.   view_data->selection_end = newsel_end;
  1504. }
  1505.  
  1506. /* ----------------------------------------------------------------------- */
  1507.  
  1508. static void _EditSky_dragstopped(void)
  1509. {
  1510.   if(null_polling) {
  1511.     nullpoll_deregister(); /* Stop auto-scrolling */
  1512.     null_polling = false;
  1513.  
  1514.     if(dragging_bands == DRAGGING_AREA) {
  1515.       /* Remove ghost caret */
  1516.       if(ghost_caret_win != -1)
  1517.         RE(_EditSky_redrawcaret(ghost_caret_win, ghost_caret_position))
  1518.  
  1519.       RE(event_deregister_wimp_handler(-1, Wimp_ENull, _EditSky_escwatch, NULL))
  1520.     }
  1521.   }
  1522. }
  1523.  
  1524. /* ----------------------------------------------------------------------- */
  1525.  
  1526. static int prepare_to_insert(ViewData *view_data)
  1527. {
  1528.   /* If no caret then replace selection */
  1529.   if(view_data->selection_exists) {
  1530.     remove_bands(&view_data->sky, view_data->selection_start, view_data->selection_end);
  1531.     return view_data->selection_start;
  1532.   } else {
  1533.     /* Otherwise undraw caret */
  1534.     int window_handle;
  1535.     if(!E(window_get_wimp_handle(0, view_data->window_id, &window_handle)))
  1536.       RE(_EditSky_redrawcaret(window_handle, view_data->caret_position))
  1537.     return view_data->caret_position;
  1538.   }
  1539. }
  1540.  
  1541. /* ----------------------------------------------------------------------- */
  1542.  
  1543. static _kernel_oserror *scroll_win_for_caret(ViewData *view_data, int window_handle)
  1544. {
  1545.   /* Scroll window if necessary to make caret visible */
  1546.   WimpGetWindowInfoBlock window;
  1547.   window.window_handle = window_handle;
  1548.   THROW(wimp_get_window_info_no_icon_data(&window))
  1549.  
  1550.   int WA_bot_of_caret = -WIN_HEIGHT + view_data->caret_position*(COL_HEIGHT+GAP);
  1551.   int visible_height = window.window_data.visible_area.ymax - window.window_data.visible_area.ymin;
  1552.   int old_scroll = window.window_data.yscroll;
  1553.   if(window.window_data.yscroll < PANE_HEIGHT+(1<<y_eigen) + WA_bot_of_caret + GAP) {
  1554.     window.window_data.yscroll = PANE_HEIGHT+(1<<y_eigen) + WA_bot_of_caret + GAP;
  1555.     /* Scroll in steps of 3 bands, if there is room */
  1556.     if(visible_height - PANE_HEIGHT-(1<<y_eigen) >= GAP + 3*(COL_HEIGHT+GAP))
  1557.       window.window_data.yscroll += 3*(COL_HEIGHT+GAP);
  1558.   } else {
  1559.     if(window.window_data.yscroll > WA_bot_of_caret + visible_height) {
  1560.       window.window_data.yscroll = WA_bot_of_caret + visible_height;
  1561.       /* Scroll in steps of 3 bands, if there is room */
  1562.       if(visible_height - PANE_HEIGHT-(1<<y_eigen) >= GAP + 3*(COL_HEIGHT+GAP))
  1563.         window.window_data.yscroll -= 3*(COL_HEIGHT+GAP);
  1564.     }
  1565.   }
  1566.   if(old_scroll != window.window_data.yscroll)
  1567.     THROW(wimp_open_window((WimpOpenWindowBlock *)&window))
  1568.  
  1569.   return NULL; /* success */
  1570. }
  1571.  
  1572. /* ----------------------------------------------------------------------- */
  1573.  
  1574. static _kernel_oserror *scroll_caret_for_win(ViewData *view_data, WimpOpenWindowBlock *win_info)
  1575. {
  1576.   int bottom_scry = (win_info->visible_area.ymax - win_info->yscroll) - WIN_HEIGHT;
  1577.   int WA_bot_of_caret = -WIN_HEIGHT + view_data->caret_position*(COL_HEIGHT+GAP);
  1578.   int new_caret_pos = view_data->caret_position;
  1579.  
  1580.   if(win_info->yscroll < PANE_HEIGHT+(1<<y_eigen) + WA_bot_of_caret + GAP)
  1581.     new_caret_pos = (win_info->visible_area.ymax -PANE_HEIGHT-(1<<y_eigen) - bottom_scry - GAP)/(COL_HEIGHT+GAP);
  1582.   else {
  1583.     if(win_info->yscroll > WA_bot_of_caret + (win_info->visible_area.ymax - win_info->visible_area.ymin))
  1584.       new_caret_pos = (win_info->visible_area.ymin - bottom_scry + COL_HEIGHT+GAP)/(COL_HEIGHT+GAP);
  1585.   }
  1586.   if(new_caret_pos != view_data->caret_position) {
  1587.     THROW(_EditSky_redrawcaret(win_info->window_handle, new_caret_pos))
  1588.     THROW(_EditSky_redrawcaret(win_info->window_handle, view_data->caret_position))
  1589.     view_data->caret_position = new_caret_pos;
  1590.   }
  1591.  
  1592.   return NULL; /* success */
  1593. }
  1594.